Skip to content

fix: skip finalization advance for stale finalized sources#802

Open
latifkasuli wants to merge 3 commits into
leanEthereum:mainfrom
latifkasuli:fix/stale-source-finalization
Open

fix: skip finalization advance for stale finalized sources#802
latifkasuli wants to merge 3 commits into
leanEthereum:mainfrom
latifkasuli:fix/stale-source-finalization

Conversation

@latifkasuli
Copy link
Copy Markdown
Contributor

Summary

This fixes an Lstar finalization edge case where an attestation with a source checkpoint at or behind the current finalized boundary could justify a newer target, then incorrectly enter the finalization-advance scan from below the finalized boundary.

Finalized sources are valid justification anchors: JustifiedSlots.is_slot_justified() treats slots at or below latest_finalized.slot as already justified. But once such an attestation crossed the supermajority threshold, the finalization logic scanned source.slot + 1 .. target.slot and called Slot.is_justifiable_after(finalized_slot) on candidate slots before finalized_slot, triggering Candidate slot must not be before finalized slot.

The fix gates finalization advancement on source.slot > finalized_slot. Stale finalized sources may still justify newer targets, but they no longer try to rewind or scan below the finalized boundary.

Validation

  • uv run --group test fill tests/consensus/lstar/state_transition/test_finalization.py --fork=Lstar --clean -q
  • uv run --group lint ruff check src/lean_spec/forks/lstar/spec.py tests/consensus/lstar/state_transition/test_finalization.py
  • uv run --group lint ruff format --check src/lean_spec/forks/lstar/spec.py tests/consensus/lstar/state_transition/test_finalization.py

@tcoratger
Copy link
Copy Markdown
Collaborator

@latifkasuli Please let me know when ready for review

@latifkasuli
Copy link
Copy Markdown
Contributor Author

Ready for review now.

I re-ran the focused validation:

  • uv run --group test fill tests/consensus/lstar/state_transition/test_finalization.py --fork=Lstar --clean -q
  • uv run --group lint ruff check src/lean_spec/forks/lstar/spec.py tests/consensus/lstar/state_transition/test_finalization.py
  • uv run --group lint ruff format --check src/lean_spec/forks/lstar/spec.py tests/consensus/lstar/state_transition/test_finalization.py

All passed.

@latifkasuli latifkasuli marked this pull request as ready for review May 30, 2026 14:05
latifkasuli and others added 2 commits May 30, 2026 17:17
Rebase the stale-source finalization fix onto current main:
the spec file moved under the spec/ package and attestation specs
renamed validator_ids to validator_indices.

Also fix the new test expectation. Building justified_slots via
model_copy(update=...) bypassed Pydantic validation, leaving data
as a list instead of a tuple, so the post-state comparison failed
even with the fix applied. Use the canonical constructor instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tcoratger tcoratger force-pushed the fix/stale-source-finalization branch from 17be451 to bf2e9d2 Compare May 30, 2026 15:19
Add the boundary case raised in review: a source whose slot equals
the finalized slot. Such a source is already final, so it may justify
a newer target but must never re-finalize. This locks the > (not >=)
intent of the finalization-advance guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants